Right drawer to edit records (#5551)
This PR introduces a new side panel to edit records and the ability to minimize the side panel. The goal is leverage this sidepanel to be able to create records while being in another show page. I'm opening the PR for feedback since it involved refactoring and therefore already touches a lot of files, even though it was quick to implement. <img width="1503" alt="Screenshot 2024-05-23 at 17 41 37" src="https://github.com/twentyhq/twenty/assets/6399865/6f17e7a8-f4e9-4eb4-b392-c756db7198ac">
This commit is contained in:
@ -1 +1,50 @@
|
||||
export { RightDrawerContainer as PageBody } from './RightDrawerContainer';
|
||||
import { ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui';
|
||||
|
||||
import { RightDrawer } from '@/ui/layout/right-drawer/components/RightDrawer';
|
||||
|
||||
import { PagePanel } from './PagePanel';
|
||||
|
||||
type PageBodyProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const StyledMainContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.noisy};
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
min-height: 0;
|
||||
padding-bottom: ${({ theme }) => theme.spacing(3)};
|
||||
padding-right: ${({ theme }) => theme.spacing(3)};
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
padding-left: ${({ theme }) => theme.spacing(3)};
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
type LeftContainerProps = {
|
||||
isRightDrawerOpen?: boolean;
|
||||
};
|
||||
|
||||
const StyledLeftContainer = styled.div<LeftContainerProps>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const PageBody = ({ children }: PageBodyProps) => (
|
||||
<StyledMainContainer>
|
||||
<StyledLeftContainer>
|
||||
<PagePanel>{children}</PagePanel>
|
||||
</StyledLeftContainer>
|
||||
<RightDrawer />
|
||||
</StyledMainContainer>
|
||||
);
|
||||
|
||||
@ -4,8 +4,8 @@ import { IconComponent } from 'twenty-ui';
|
||||
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
import { PageBody } from './PageBody';
|
||||
import { PageHeader } from './PageHeader';
|
||||
import { RightDrawerContainer } from './RightDrawerContainer';
|
||||
|
||||
type SubMenuTopBarContainerProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
@ -32,7 +32,7 @@ export const SubMenuTopBarContainer = ({
|
||||
return (
|
||||
<StyledContainer isMobile={isMobile} className={className}>
|
||||
{isMobile && <PageHeader title={title} Icon={Icon} />}
|
||||
<RightDrawerContainer>{children}</RightDrawerContainer>
|
||||
<PageBody>{children}</PageBody>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@ import { Key } from 'ts-key-enum';
|
||||
|
||||
import { RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID } from '@/ui/layout/right-drawer/constants/RightDrawerClickOutsideListener';
|
||||
import { isRightDrawerAnimationCompletedState } from '@/ui/layout/right-drawer/states/isRightDrawerAnimationCompleted';
|
||||
import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState';
|
||||
import { rightDrawerCloseEventState } from '@/ui/layout/right-drawer/states/rightDrawerCloseEventsState';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
|
||||
@ -46,6 +47,8 @@ export const RightDrawer = () => {
|
||||
isRightDrawerOpenState,
|
||||
);
|
||||
|
||||
const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState);
|
||||
|
||||
const isRightDrawerExpanded = useRecoilValue(isRightDrawerExpandedState);
|
||||
const [, setIsRightDrawerAnimationCompleted] = useRecoilState(
|
||||
isRightDrawerAnimationCompletedState,
|
||||
@ -69,8 +72,11 @@ export const RightDrawer = () => {
|
||||
const isRightDrawerOpen = snapshot
|
||||
.getLoadable(isRightDrawerOpenState)
|
||||
.getValue();
|
||||
const isRightDrawerMinimized = snapshot
|
||||
.getLoadable(isRightDrawerMinimizedState)
|
||||
.getValue();
|
||||
|
||||
if (isRightDrawerOpen) {
|
||||
if (isRightDrawerOpen && !isRightDrawerMinimized) {
|
||||
set(rightDrawerCloseEventState, event);
|
||||
closeRightDrawer();
|
||||
}
|
||||
@ -115,6 +121,13 @@ export const RightDrawer = () => {
|
||||
closed: {
|
||||
x: '100%',
|
||||
},
|
||||
minimized: {
|
||||
x: '0%',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
bottom: '0',
|
||||
top: 'auto',
|
||||
},
|
||||
};
|
||||
const handleAnimationComplete = () => {
|
||||
setIsRightDrawerAnimationCompleted(isRightDrawerOpen);
|
||||
@ -122,8 +135,20 @@ export const RightDrawer = () => {
|
||||
|
||||
return (
|
||||
<StyledContainer
|
||||
initial="closed"
|
||||
animate={isRightDrawerOpen ? 'normal' : 'closed'}
|
||||
initial={
|
||||
isRightDrawerOpen
|
||||
? isRightDrawerMinimized
|
||||
? 'minimized'
|
||||
: 'normal'
|
||||
: 'closed'
|
||||
}
|
||||
animate={
|
||||
isRightDrawerOpen
|
||||
? isRightDrawerMinimized
|
||||
? 'minimized'
|
||||
: 'normal'
|
||||
: 'closed'
|
||||
}
|
||||
variants={variants}
|
||||
transition={{
|
||||
duration: theme.animation.duration.normal,
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { RightDrawerCalendarEvent } from '@/activities/calendar/right-drawer/components/RightDrawerCalendarEvent';
|
||||
import { RightDrawerEmailThread } from '@/activities/emails/right-drawer/components/RightDrawerEmailThread';
|
||||
import { RightDrawerCreateActivity } from '@/activities/right-drawer/components/create/RightDrawerCreateActivity';
|
||||
import { RightDrawerEditActivity } from '@/activities/right-drawer/components/edit/RightDrawerEditActivity';
|
||||
import { RightDrawerRecord } from '@/object-record/record-right-drawer/components/RightDrawerRecord';
|
||||
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
|
||||
import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState';
|
||||
|
||||
import { RightDrawerActivityTopBar } from '../../../../activities/right-drawer/components/RightDrawerActivityTopBar';
|
||||
import { rightDrawerPageState } from '../states/rightDrawerPageState';
|
||||
import { RightDrawerPages } from '../types/RightDrawerPages';
|
||||
|
||||
@ -30,19 +32,23 @@ const StyledRightDrawerBody = styled.div`
|
||||
const RIGHT_DRAWER_PAGES_CONFIG = {
|
||||
[RightDrawerPages.CreateActivity]: {
|
||||
page: <RightDrawerCreateActivity />,
|
||||
topBar: <RightDrawerActivityTopBar />,
|
||||
topBar: <RightDrawerTopBar page={RightDrawerPages.CreateActivity} />,
|
||||
},
|
||||
[RightDrawerPages.EditActivity]: {
|
||||
page: <RightDrawerEditActivity />,
|
||||
topBar: <RightDrawerActivityTopBar />,
|
||||
topBar: <RightDrawerTopBar page={RightDrawerPages.EditActivity} />,
|
||||
},
|
||||
[RightDrawerPages.ViewEmailThread]: {
|
||||
page: <RightDrawerEmailThread />,
|
||||
topBar: <RightDrawerActivityTopBar showActionBar={false} />,
|
||||
topBar: <RightDrawerTopBar page={RightDrawerPages.ViewEmailThread} />,
|
||||
},
|
||||
[RightDrawerPages.ViewCalendarEvent]: {
|
||||
page: <RightDrawerCalendarEvent />,
|
||||
topBar: <RightDrawerActivityTopBar showActionBar={false} />,
|
||||
topBar: <RightDrawerTopBar page={RightDrawerPages.ViewCalendarEvent} />,
|
||||
},
|
||||
[RightDrawerPages.ViewRecord]: {
|
||||
page: <RightDrawerRecord />,
|
||||
topBar: <RightDrawerTopBar page={RightDrawerPages.ViewRecord} />,
|
||||
},
|
||||
};
|
||||
|
||||
@ -53,10 +59,14 @@ export const RightDrawerRouter = () => {
|
||||
? RIGHT_DRAWER_PAGES_CONFIG[rightDrawerPage]
|
||||
: {};
|
||||
|
||||
const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState);
|
||||
|
||||
return (
|
||||
<StyledRightDrawerPage>
|
||||
{topBar}
|
||||
<StyledRightDrawerBody>{page}</StyledRightDrawerBody>
|
||||
{!isRightDrawerMinimized && (
|
||||
<StyledRightDrawerBody>{page}</StyledRightDrawerBody>
|
||||
)}
|
||||
</StyledRightDrawerPage>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,116 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Chip, ChipAccent, ChipSize, useIcons } from 'twenty-ui';
|
||||
|
||||
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
|
||||
import { RightDrawerTopBarCloseButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarCloseButton';
|
||||
import { RightDrawerTopBarExpandButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarExpandButton';
|
||||
import { RightDrawerTopBarMinimizeButton } from '@/ui/layout/right-drawer/components/RightDrawerTopBarMinimizeButton';
|
||||
import { StyledRightDrawerTopBar } from '@/ui/layout/right-drawer/components/StyledRightDrawerTopBar';
|
||||
import { RIGHT_DRAWER_PAGE_ICONS } from '@/ui/layout/right-drawer/constants/RightDrawerPageIcons';
|
||||
import { RIGHT_DRAWER_PAGE_TITLES } from '@/ui/layout/right-drawer/constants/RightDrawerPageTitles';
|
||||
import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState';
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
const StyledTopBarWrapper = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledMinimizeTopBarTitleContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: 24px;
|
||||
width: 168px;
|
||||
`;
|
||||
|
||||
const StyledMinimizeTopBarTitle = styled.div`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledMinimizeTopBarIcon = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const RightDrawerTopBar = ({ page }: { page: RightDrawerPages }) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const [isRightDrawerMinimized, setIsRightDrawerMinimized] = useRecoilState(
|
||||
isRightDrawerMinimizedState,
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const handleOnclick = () => {
|
||||
if (isRightDrawerMinimized) {
|
||||
setIsRightDrawerMinimized(false);
|
||||
}
|
||||
};
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const PageIcon = getIcon(RIGHT_DRAWER_PAGE_ICONS[page]);
|
||||
|
||||
const viewableRecordNameSingular = useRecoilValue(
|
||||
viewableRecordNameSingularState,
|
||||
);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: viewableRecordNameSingular ?? 'company',
|
||||
});
|
||||
|
||||
const ObjectIcon = getIcon(objectMetadataItem.icon);
|
||||
|
||||
const label =
|
||||
page === RightDrawerPages.ViewRecord
|
||||
? objectMetadataItem.labelSingular
|
||||
: RIGHT_DRAWER_PAGE_TITLES[page];
|
||||
|
||||
const Icon = page === RightDrawerPages.ViewRecord ? ObjectIcon : PageIcon;
|
||||
|
||||
return (
|
||||
<StyledRightDrawerTopBar
|
||||
onClick={handleOnclick}
|
||||
isRightDrawerMinimized={isRightDrawerMinimized}
|
||||
>
|
||||
{!isRightDrawerMinimized &&
|
||||
(page === RightDrawerPages.EditActivity ||
|
||||
page === RightDrawerPages.CreateActivity) && <ActivityActionBar />}
|
||||
{!isRightDrawerMinimized &&
|
||||
page !== RightDrawerPages.EditActivity &&
|
||||
page !== RightDrawerPages.CreateActivity && (
|
||||
<Chip
|
||||
label={label}
|
||||
leftComponent={<Icon size={theme.icon.size.md} />}
|
||||
size={ChipSize.Large}
|
||||
accent={ChipAccent.TextSecondary}
|
||||
clickable={false}
|
||||
/>
|
||||
)}
|
||||
{isRightDrawerMinimized && (
|
||||
<StyledMinimizeTopBarTitleContainer>
|
||||
<StyledMinimizeTopBarIcon>
|
||||
<Icon size={theme.icon.size.md} />
|
||||
</StyledMinimizeTopBarIcon>
|
||||
<StyledMinimizeTopBarTitle>{label}</StyledMinimizeTopBarTitle>
|
||||
</StyledMinimizeTopBarTitleContainer>
|
||||
)}
|
||||
<StyledTopBarWrapper>
|
||||
{!isMobile && !isRightDrawerMinimized && (
|
||||
<RightDrawerTopBarMinimizeButton />
|
||||
)}
|
||||
{!isMobile && !isRightDrawerMinimized && (
|
||||
<RightDrawerTopBarExpandButton />
|
||||
)}
|
||||
<RightDrawerTopBarCloseButton />
|
||||
</StyledTopBarWrapper>
|
||||
</StyledRightDrawerTopBar>
|
||||
);
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
import { IconChevronsRight } from 'twenty-ui';
|
||||
import { IconX } from 'twenty-ui';
|
||||
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
|
||||
@ -13,7 +13,7 @@ export const RightDrawerTopBarCloseButton = () => {
|
||||
|
||||
return (
|
||||
<LightIconButton
|
||||
Icon={IconChevronsRight}
|
||||
Icon={IconX}
|
||||
onClick={handleButtonClick}
|
||||
size="medium"
|
||||
accent="tertiary"
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
import {
|
||||
IconLayoutSidebarRightCollapse,
|
||||
IconLayoutSidebarRightExpand,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
|
||||
import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
|
||||
export const RightDrawerTopBarExpandButton = () => {
|
||||
const [isRightDrawerExpanded, setIsRightDrawerExpanded] = useRecoilState(
|
||||
isRightDrawerExpandedState,
|
||||
);
|
||||
const { isRightDrawerExpanded, downsizeRightDrawer, expandRightDrawer } =
|
||||
useRightDrawer();
|
||||
|
||||
const handleButtonClick = () => {
|
||||
setIsRightDrawerExpanded(!isRightDrawerExpanded);
|
||||
if (isRightDrawerExpanded === true) {
|
||||
downsizeRightDrawer();
|
||||
return;
|
||||
}
|
||||
expandRightDrawer();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { IconMinus } from 'twenty-ui';
|
||||
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
|
||||
export const RightDrawerTopBarMinimizeButton = () => {
|
||||
const { isRightDrawerMinimized, minimizeRightDrawer, maximizeRightDrawer } =
|
||||
useRightDrawer();
|
||||
|
||||
const handleButtonClick = () => {
|
||||
isRightDrawerMinimized ? maximizeRightDrawer() : minimizeRightDrawer();
|
||||
};
|
||||
|
||||
return (
|
||||
<LightIconButton
|
||||
Icon={IconMinus}
|
||||
onClick={handleButtonClick}
|
||||
size="medium"
|
||||
accent="tertiary"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,6 +1,8 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const StyledRightDrawerTopBar = styled.div`
|
||||
export const StyledRightDrawerTopBar = styled.div<{
|
||||
isRightDrawerMinimized: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
@ -9,9 +11,12 @@ export const StyledRightDrawerTopBar = styled.div`
|
||||
flex-direction: row;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: 56px;
|
||||
height: ${({ isRightDrawerMinimized }) =>
|
||||
isRightDrawerMinimized ? '40px' : '56px'};
|
||||
justify-content: space-between;
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
cursor: ${({ isRightDrawerMinimized }) =>
|
||||
isRightDrawerMinimized ? 'pointer' : 'default'};
|
||||
`;
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
|
||||
import { RightDrawerTopBar } from '../RightDrawerTopBar';
|
||||
|
||||
const meta: Meta<typeof RightDrawerTopBar> = {
|
||||
title: 'Modules/Activities/RightDrawer/RightDrawerActivityTopBar',
|
||||
component: RightDrawerTopBar,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ width: '500px' }}>
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
ComponentWithRouterDecorator,
|
||||
],
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RightDrawerTopBar>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@ -0,0 +1,9 @@
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
|
||||
export const RIGHT_DRAWER_PAGE_ICONS = {
|
||||
[RightDrawerPages.CreateActivity]: 'IconNote',
|
||||
[RightDrawerPages.EditActivity]: 'IconNote',
|
||||
[RightDrawerPages.ViewEmailThread]: 'IconMail',
|
||||
[RightDrawerPages.ViewCalendarEvent]: 'IconCalendarEvent',
|
||||
[RightDrawerPages.ViewRecord]: 'Icon123',
|
||||
};
|
||||
@ -0,0 +1,9 @@
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
|
||||
export const RIGHT_DRAWER_PAGE_TITLES = {
|
||||
[RightDrawerPages.CreateActivity]: 'Create Activity',
|
||||
[RightDrawerPages.EditActivity]: 'Edit Activity',
|
||||
[RightDrawerPages.ViewEmailThread]: 'Email Thread',
|
||||
[RightDrawerPages.ViewCalendarEvent]: 'Calendar Event',
|
||||
[RightDrawerPages.ViewRecord]: 'Record Editor',
|
||||
};
|
||||
@ -1,5 +1,6 @@
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
|
||||
import { isRightDrawerMinimizedState } from '@/ui/layout/right-drawer/states/isRightDrawerMinimizedState';
|
||||
import { rightDrawerCloseEventState } from '@/ui/layout/right-drawer/states/rightDrawerCloseEventsState';
|
||||
|
||||
import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState';
|
||||
@ -9,6 +10,8 @@ import { RightDrawerPages } from '../types/RightDrawerPages';
|
||||
|
||||
export const useRightDrawer = () => {
|
||||
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
||||
const [isRightDrawerExpanded] = useRecoilState(isRightDrawerExpandedState);
|
||||
const [isRightDrawerMinimized] = useRecoilState(isRightDrawerMinimizedState);
|
||||
|
||||
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
|
||||
|
||||
@ -18,6 +21,7 @@ export const useRightDrawer = () => {
|
||||
set(rightDrawerPageState, rightDrawerPage);
|
||||
set(isRightDrawerExpandedState, false);
|
||||
set(isRightDrawerOpenState, true);
|
||||
set(isRightDrawerMinimizedState, false);
|
||||
},
|
||||
[],
|
||||
);
|
||||
@ -27,6 +31,47 @@ export const useRightDrawer = () => {
|
||||
() => {
|
||||
set(isRightDrawerExpandedState, false);
|
||||
set(isRightDrawerOpenState, false);
|
||||
set(isRightDrawerMinimizedState, false);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const minimizeRightDrawer = useRecoilCallback(
|
||||
({ set }) =>
|
||||
() => {
|
||||
set(isRightDrawerExpandedState, false);
|
||||
set(isRightDrawerOpenState, true);
|
||||
set(isRightDrawerMinimizedState, true);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const maximizeRightDrawer = useRecoilCallback(
|
||||
({ set }) =>
|
||||
() => {
|
||||
set(isRightDrawerMinimizedState, false);
|
||||
set(isRightDrawerExpandedState, false);
|
||||
set(isRightDrawerOpenState, true);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const expandRightDrawer = useRecoilCallback(
|
||||
({ set }) =>
|
||||
() => {
|
||||
set(isRightDrawerExpandedState, true);
|
||||
set(isRightDrawerOpenState, true);
|
||||
set(isRightDrawerMinimizedState, false);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const downsizeRightDrawer = useRecoilCallback(
|
||||
({ set }) =>
|
||||
() => {
|
||||
set(isRightDrawerExpandedState, false);
|
||||
set(isRightDrawerOpenState, true);
|
||||
set(isRightDrawerMinimizedState, false);
|
||||
},
|
||||
[],
|
||||
);
|
||||
@ -50,8 +95,14 @@ export const useRightDrawer = () => {
|
||||
return {
|
||||
rightDrawerPage,
|
||||
isRightDrawerOpen,
|
||||
isRightDrawerExpanded,
|
||||
isRightDrawerMinimized,
|
||||
openRightDrawer,
|
||||
closeRightDrawer,
|
||||
minimizeRightDrawer,
|
||||
maximizeRightDrawer,
|
||||
expandRightDrawer,
|
||||
downsizeRightDrawer,
|
||||
isSameEventThanRightDrawerClose,
|
||||
};
|
||||
};
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
|
||||
export const isRightDrawerMinimizedState = createState<boolean>({
|
||||
key: 'ui/layout/is-right-drawer-minimized',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -3,4 +3,5 @@ export enum RightDrawerPages {
|
||||
EditActivity = 'edit-activity',
|
||||
ViewEmailThread = 'view-email-thread',
|
||||
ViewCalendarEvent = 'view-calendar-event',
|
||||
ViewRecord = 'view-record',
|
||||
}
|
||||
|
||||
@ -4,22 +4,23 @@ import styled from '@emotion/styled';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
|
||||
const StyledOuterContainer = styled.div`
|
||||
const StyledOuterContainer = styled.div<{ isMobile: boolean }>`
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom-left-radius: 8px;
|
||||
border-right: ${({ theme }) =>
|
||||
useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`};
|
||||
border-right: ${({ theme, isMobile }) =>
|
||||
isMobile ? 'none' : `1px solid ${theme.border.color.medium}`};
|
||||
border-top-left-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(3)};
|
||||
z-index: 10;
|
||||
width: 'auto';
|
||||
`;
|
||||
|
||||
const StyledInnerContainer = styled.div`
|
||||
const StyledInnerContainer = styled.div<{ isMobile: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: ${() => (useIsMobile() ? `100%` : '348px')};
|
||||
width: ${({ isMobile }) => (isMobile ? `100%` : '348px')};
|
||||
`;
|
||||
|
||||
const StyledIntermediateContainer = styled.div`
|
||||
@ -29,24 +30,30 @@ const StyledIntermediateContainer = styled.div`
|
||||
`;
|
||||
|
||||
export type ShowPageLeftContainerProps = {
|
||||
forceMobile: boolean;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const ShowPageLeftContainer = ({
|
||||
forceMobile = false,
|
||||
children,
|
||||
}: ShowPageLeftContainerProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
return isMobile ? (
|
||||
<StyledOuterContainer>
|
||||
<StyledInnerContainer>{children}</StyledInnerContainer>
|
||||
</StyledOuterContainer>
|
||||
) : (
|
||||
<StyledOuterContainer>
|
||||
<ScrollWrapper>
|
||||
<StyledIntermediateContainer>
|
||||
<StyledInnerContainer>{children}</StyledInnerContainer>
|
||||
</StyledIntermediateContainer>
|
||||
</ScrollWrapper>
|
||||
const isMobile = useIsMobile() || forceMobile;
|
||||
return (
|
||||
<StyledOuterContainer isMobile={isMobile}>
|
||||
{isMobile ? (
|
||||
<StyledInnerContainer isMobile={isMobile}>
|
||||
{children}
|
||||
</StyledInnerContainer>
|
||||
) : (
|
||||
<ScrollWrapper>
|
||||
<StyledIntermediateContainer>
|
||||
<StyledInnerContainer isMobile={isMobile}>
|
||||
{children}
|
||||
</StyledInnerContainer>
|
||||
</StyledIntermediateContainer>
|
||||
</ScrollWrapper>
|
||||
)}
|
||||
</StyledOuterContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -24,12 +24,12 @@ import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
|
||||
const StyledShowPageRightContainer = styled.div`
|
||||
const StyledShowPageRightContainer = styled.div<{ isMobile: boolean }>`
|
||||
display: flex;
|
||||
flex: 1 0 0;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
overflow: ${() => (useIsMobile() ? 'none' : 'hidden')};
|
||||
overflow: ${(isMobile) => (isMobile ? 'none' : 'hidden')};
|
||||
width: calc(100% + 4px);
|
||||
`;
|
||||
|
||||
@ -53,6 +53,8 @@ type ShowPageRightContainerProps = {
|
||||
tasks?: boolean;
|
||||
notes?: boolean;
|
||||
emails?: boolean;
|
||||
summary?: JSX.Element;
|
||||
isRightDrawer?: boolean;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
@ -63,8 +65,12 @@ export const ShowPageRightContainer = ({
|
||||
notes,
|
||||
emails,
|
||||
loading,
|
||||
summary,
|
||||
isRightDrawer = false,
|
||||
}: ShowPageRightContainerProps) => {
|
||||
const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
|
||||
const { activeTabIdState } = useTabList(
|
||||
TAB_LIST_COMPONENT_ID + isRightDrawer,
|
||||
);
|
||||
const activeTabId = useRecoilValue(activeTabIdState);
|
||||
|
||||
const shouldDisplayCalendarTab =
|
||||
@ -80,12 +86,20 @@ export const ShowPageRightContainer = ({
|
||||
CoreObjectNameSingular.Company) ||
|
||||
targetableObject.targetObjectNameSingular === CoreObjectNameSingular.Person;
|
||||
|
||||
const isMobile = useIsMobile() || isRightDrawer;
|
||||
|
||||
const TASK_TABS = [
|
||||
{
|
||||
id: 'summary',
|
||||
title: 'Summary',
|
||||
Icon: IconCheckbox,
|
||||
hide: !isMobile,
|
||||
},
|
||||
{
|
||||
id: 'timeline',
|
||||
title: 'Timeline',
|
||||
Icon: IconTimelineEvent,
|
||||
hide: !timeline,
|
||||
hide: !timeline || isRightDrawer,
|
||||
},
|
||||
{
|
||||
id: 'tasks',
|
||||
@ -127,14 +141,15 @@ export const ShowPageRightContainer = ({
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledShowPageRightContainer>
|
||||
<StyledShowPageRightContainer isMobile={isMobile}>
|
||||
<StyledTabListContainer>
|
||||
<TabList
|
||||
loading={loading}
|
||||
tabListId={TAB_LIST_COMPONENT_ID}
|
||||
tabListId={TAB_LIST_COMPONENT_ID + isRightDrawer}
|
||||
tabs={TASK_TABS}
|
||||
/>
|
||||
</StyledTabListContainer>
|
||||
{activeTabId === 'summary' && summary}
|
||||
{activeTabId === 'timeline' && (
|
||||
<>
|
||||
<TimelineQueryEffect targetableObject={targetableObject} />
|
||||
@ -157,6 +172,7 @@ export const ShowPageRightContainer = ({
|
||||
{activeTabId === 'logs' && (
|
||||
<TimelineActivities targetableObject={targetableObject} />
|
||||
)}
|
||||
{}
|
||||
</StyledShowPageRightContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -35,7 +35,7 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const TabList = ({ tabs, tabListId, loading }: TabListProps) => {
|
||||
const initialActiveTabId = tabs[0].id;
|
||||
const initialActiveTabId = tabs.find((tab) => !tab.hide)?.id || '';
|
||||
|
||||
const { activeTabIdState, setActiveTabId } = useTabList(tabListId);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user