Feat/add right drawer (#159)

* Added right drawer component and logic

* Refactored layout to accept right drawer
This commit is contained in:
Lucas Bordeau
2023-05-30 21:03:50 +02:00
committed by GitHub
parent 3674365e6f
commit cb259d5cf1
24 changed files with 272 additions and 39 deletions

View File

@ -24,7 +24,7 @@ function App() {
}, [data]);
return (
<div>
<>
{
<AppLayout user={user}>
<Routes>
@ -65,7 +65,7 @@ function App() {
</Routes>
</AppLayout>
}
</div>
</>
);
}

View File

@ -0,0 +1,9 @@
import { RightDrawerTopBar } from '../../layout/right-drawer/RightDrawerTopBar';
export function RightDrawerComments() {
return (
<>
<RightDrawerTopBar title="Comments" />
</>
);
}

View File

@ -17,7 +17,6 @@ const StyledContainer = styled.div`
align-items: center;
padding-left: ${(props) => props.theme.spacing(2)};
padding-right: ${(props) => props.theme.spacing(2)};
color: ${(props) => props.theme.red};
left: 50%;
transform: translateX(-50%);

View File

@ -4,14 +4,20 @@ import { ReactNode } from 'react';
type OwnProps = {
icon: ReactNode;
label: string;
type?: 'standard' | 'warning';
onClick: () => void;
};
const StyledButton = styled.div`
type StyledButtonProps = {
type: 'standard' | 'warning';
};
const StyledButton = styled.div<StyledButtonProps>`
display: flex;
cursor: pointer;
user-select: none;
color: ${(props) =>
props.type === 'warning' ? props.theme.red : props.theme.text60};
justify-content: center;
padding: ${(props) => props.theme.spacing(2)};
@ -28,9 +34,14 @@ const StyledButtonLabel = styled.div`
font-weight: 500;
`;
export function EntityTableActionBarButton({ label, icon, onClick }: OwnProps) {
export function EntityTableActionBarButton({
label,
icon,
type = 'standard',
onClick,
}: OwnProps) {
return (
<StyledButton onClick={onClick}>
<StyledButton type={type} onClick={onClick}>
{icon}
<StyledButtonLabel>{label}</StyledButtonLabel>
</StyledButton>

View File

@ -0,0 +1,19 @@
import { FaRegComment } from 'react-icons/fa';
import { EntityTableActionBarButton } from './EntityTableActionBarButton';
import { useOpenRightDrawer } from '../../../modules/ui/layout/right-drawer/hooks/useOpenRightDrawer';
export function TableActionBarButtonToggleComments() {
const openRightDrawer = useOpenRightDrawer();
async function handleButtonClick() {
openRightDrawer('comments');
}
return (
<EntityTableActionBarButton
label="Comment"
icon={<FaRegComment size={16} />}
onClick={handleButtonClick}
/>
);
}

View File

@ -1,4 +1,4 @@
import Navbar from './navbar/Navbar';
import { Navbar } from './navbar/Navbar';
import styled from '@emotion/styled';
import { ThemeProvider } from '@emotion/react';
import { User } from '../interfaces/entities/user.interface';
@ -9,13 +9,15 @@ const StyledLayout = styled.div`
flex-direction: row;
width: 100vw;
height: 100vh;
background: ${(props) => props.theme.noisyBackground};
overflow: hidden;
`;
const StyledRightContainer = styled.div`
const MainContainer = styled.div`
display: flex;
flex-direction: row;
flex: 1;
overflow: hidden;
widht: calc(100% - 220px);
`;
type OwnProps = {
@ -28,7 +30,7 @@ function AppLayout({ children, user }: OwnProps) {
<ThemeProvider theme={lightTheme}>
<StyledLayout>
<Navbar user={user} workspace={user?.workspaceMember?.workspace} />
<StyledRightContainer>{children}</StyledRightContainer>
<MainContainer>{children}</MainContainer>
</StyledLayout>
</ThemeProvider>
);

View File

@ -0,0 +1,14 @@
import styled from '@emotion/styled';
import React from 'react';
const StyledPanel = styled.div`
display: flex;
background: ${(props) => props.theme.primaryBackground};
border-radius: 8px;
border: 1px solid ${(props) => props.theme.primaryBorder};
width: 100%;
`;
export function Panel({ children }: { children: React.ReactNode }) {
return <StyledPanel>{children}</StyledPanel>;
}

View File

@ -0,0 +1,12 @@
import styled from '@emotion/styled';
import React from 'react';
const StyledMainContainer = styled.div`
display: flex;
flex-direction: row;
overflow: hidden;
`;
export function MainContainer({ children }: { children: React.ReactNode }) {
return <StyledMainContainer>{children}</StyledMainContainer>;
}

View File

@ -1,6 +1,10 @@
import styled from '@emotion/styled';
import TopBar from '../top-bar/TopBar';
import { ReactNode } from 'react';
import { RightDrawer } from '../right-drawer/RightDrawer';
import { Panel } from '../Panel';
import { isRightDrawerOpenState } from '../../modules/ui/layout/right-drawer/states/isRightDrawerOpenState';
import { useRecoilState } from 'recoil';
type OwnProps = {
children: JSX.Element;
@ -11,30 +15,32 @@ type OwnProps = {
const StyledContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
width: 100%;
`;
const ContentContainer = styled.div`
const MainContainer = styled.div`
display: flex;
position: relative;
flex-direction: column;
background: ${(props) => props.theme.noisyBackground};
flex: 1;
padding-right: ${(props) => props.theme.spacing(3)};
padding-bottom: ${(props) => props.theme.spacing(3)};
width: calc(100% - ${(props) => props.theme.spacing(3)});
height: calc(100% - 54px);
`;
const ContentSubContainer = styled.div`
display: flex;
background: ${(props) => props.theme.primaryBackground};
border-radius: 8px;
flex-direction: row;
height: 100%;
flex: 1;
border: 1px solid ${(props) => props.theme.primaryBorder};
width: calc(100% - 16px);
margin-right: 8px;
margin-bottom: 8px;
gap: 8px;
padding: 4px;
`;
const RIGHT_DRAWER_WIDTH = '300px';
type LeftContainerProps = {
isRightDrawerOpen?: boolean;
};
const LeftContainer = styled.div<LeftContainerProps>`
display: flex;
width: calc(
100% - ${(props) => (props.isRightDrawerOpen ? RIGHT_DRAWER_WIDTH : '0px')}
);
`;
function FullWidthContainer({
@ -43,12 +49,17 @@ function FullWidthContainer({
icon,
onAddButtonClick,
}: OwnProps) {
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
return (
<StyledContainer>
<TopBar title={title} icon={icon} onAddButtonClick={onAddButtonClick} />
<ContentContainer>
<ContentSubContainer>{children}</ContentSubContainer>
</ContentContainer>
<MainContainer>
<LeftContainer isRightDrawerOpen={isRightDrawerOpen}>
<Panel>{children}</Panel>
</LeftContainer>
<RightDrawer />
</MainContainer>
</StyledContainer>
);
}

View File

@ -10,9 +10,9 @@ import { TbBuilding, TbUser } from 'react-icons/tb';
const NavbarContainer = styled.div`
display: flex;
flex-direction: column;
background: ${(props) => props.theme.noisyBackground};
min-width: 220px;
width: 220px;
padding: ${(props) => props.theme.spacing(2)};
flex-shrink: 0;
`;
const NavItemsContainer = styled.div`
@ -26,7 +26,7 @@ type OwnProps = {
workspace?: Workspace;
};
function Navbar({ workspace }: OwnProps) {
export function Navbar({ workspace }: OwnProps) {
return (
<>
<NavbarContainer>
@ -60,5 +60,3 @@ function Navbar({ workspace }: OwnProps) {
</>
);
}
export default Navbar;

View File

@ -0,0 +1,30 @@
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { isRightDrawerOpenState } from '../../modules/ui/layout/right-drawer/states/isRightDrawerOpenState';
import { RightDrawerRouter } from './RightDrawerRouter';
import { rightDrawerPageState } from '../../modules/ui/layout/right-drawer/states/rightDrawerPageState';
import { isDefined } from '../../modules/utils/type-guards/isDefined';
import { Panel } from '../Panel';
const StyledRightDrawer = styled.div`
display: flex;
flex-direction: row;
width: 300px;
`;
export function RightDrawer() {
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
if (!isRightDrawerOpen || !isDefined(rightDrawerPage)) {
return <></>;
}
return (
<StyledRightDrawer>
<Panel>
<RightDrawerRouter />
</Panel>
</StyledRightDrawer>
);
}

View File

@ -0,0 +1,14 @@
import { useRecoilState } from 'recoil';
import { rightDrawerPageState } from '../../modules/ui/layout/right-drawer/states/rightDrawerPageState';
import { isDefined } from '../../modules/utils/type-guards/isDefined';
import { RightDrawerComments } from '../../components/comments/RightDrawerComments';
export function RightDrawerRouter() {
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
if (!isDefined(rightDrawerPage)) {
return <></>;
}
return rightDrawerPage === 'comments' ? <RightDrawerComments /> : <></>;
}

View File

@ -0,0 +1,34 @@
import styled from '@emotion/styled';
import { RightDrawerTopBarCloseButton } from './RightDrawerTopBarCloseButton';
const StyledRightDrawerTopBar = styled.div`
display: flex;
flex-direction: row;
height: 40px;
align-items: center;
justify-content: space-between;
padding-left: 8px;
padding-right: 8px;
font-size: 13px;
color: ${(props) => props.theme.text60};
border-bottom: 1px solid ${(props) => props.theme.lightBorder};
width: 100%;
`;
const StyledTopBarTitle = styled.div`
align-items: center;
font-weight: 500;
`;
export function RightDrawerTopBar({
title,
}: {
title: string | null | undefined;
}) {
return (
<StyledRightDrawerTopBar>
<StyledTopBarTitle>{title}</StyledTopBarTitle>
<RightDrawerTopBarCloseButton />
</StyledRightDrawerTopBar>
);
}

View File

@ -0,0 +1,37 @@
import styled from '@emotion/styled';
import { FaTimes } from 'react-icons/fa';
import { useRecoilState } from 'recoil';
import { isRightDrawerOpenState } from '../../modules/ui/layout/right-drawer/states/isRightDrawerOpenState';
const StyledButton = styled.button`
height: 24px;
width: 24px;
border: 1px solid ${(props) => props.theme.lightBorder};
background: none;
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
padding: 8px;
border-radius: 4px;
transition: ${(props) => props.theme.clickableElementBackgroundTransition};
&:hover {
background: ${(props) => props.theme.clickableElementBackgroundHover};
}
`;
export function RightDrawerTopBarCloseButton() {
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
function handleButtonClick() {
setIsRightDrawerOpen(false);
}
return (
<StyledButton onClick={handleButtonClick}>
<FaTimes />
</StyledButton>
);
}

View File

@ -30,6 +30,10 @@ const lightThemeSpecific = {
secondaryBackgroundSmallTransparency: 'rgba(252, 252, 252, 0.97)',
primaryBorder: 'rgba(0, 0, 0, 0.08)',
lightBorder: '#f5f5f5',
clickableElementBackgroundHover: 'rgba(0, 0, 0, 0.04)',
clickableElementBackgroundTransition: 'background 0.1s ease',
text100: '#000',
text80: '#333333',
@ -62,7 +66,11 @@ const darkThemeSpecific: typeof lightThemeSpecific = {
secondaryBackgroundSmallTransparency: 'rgba(23, 23, 23, 0.97)',
clickableElementBackgroundHover: 'rgba(0, 0, 0, 0.04)',
clickableElementBackgroundTransition: 'background 0.1s ease',
primaryBorder: 'rgba(255, 255, 255, 0.08)',
lightBorder: '#222222',
text100: '#ffffff',
text80: '#cccccc',

View File

@ -11,7 +11,6 @@ const TopBarContainer = styled.div`
padding: 8px;
font-size: 14px;
color: ${(props) => props.theme.text80};
flex-shrink: 0;
`;
const TitleContainer = styled.div`

View File

@ -0,0 +1,14 @@
import { useRecoilState } from 'recoil';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerPage } from '../types/RightDrawerPage';
export function useOpenRightDrawer() {
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const [, setRightDrawerPage] = useRecoilState(rightDrawerPageState);
return function openRightDrawer(rightDrawerPage: RightDrawerPage) {
setRightDrawerPage(rightDrawerPage);
setIsRightDrawerOpen(true);
};
}

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const isRightDrawerOpenState = atom<boolean>({
key: 'ui/layout/is-right-drawer-open',
default: false,
});

View File

@ -0,0 +1,7 @@
import { atom } from 'recoil';
import { RightDrawerPage } from '../types/RightDrawerPage';
export const rightDrawerPageState = atom<RightDrawerPage | null>({
key: 'ui/layout/right-drawer-page',
default: 'comments',
});

View File

@ -0,0 +1 @@
export type RightDrawerPage = 'comments';

View File

@ -28,6 +28,7 @@ import { availableFilters } from './companies-filters';
import { TbBuilding } from 'react-icons/tb';
import { EntityTableActionBar } from '../../components/table/action-bar/EntityTableActionBar';
import { TableActionBarButtonDeleteCompanies } from './table/TableActionBarButtonDeleteCompanies';
import { TableActionBarButtonToggleComments } from '../../components/table/action-bar/TableActionBarButtonOpenComments';
const StyledCompaniesContainer = styled.div`
display: flex;
@ -91,6 +92,7 @@ function Companies() {
/>
</StyledCompaniesContainer>
<EntityTableActionBar>
<TableActionBarButtonToggleComments />
<TableActionBarButtonDeleteCompanies />
</EntityTableActionBar>
</>

View File

@ -4,9 +4,11 @@ import { useDeleteCompaniesMutation } from '../../../generated/graphql';
import { selectedRowIdsState } from '../../../modules/ui/tables/states/selectedRowIdsState';
import { useRecoilValue } from 'recoil';
import { useResetTableRowSelection } from '../../../modules/ui/tables/hooks/useResetTableRowSelection';
import { useTheme } from '@emotion/react';
export function TableActionBarButtonDeleteCompanies() {
const selectedRowIds = useRecoilValue(selectedRowIdsState);
const theme = useTheme();
const resetRowSelection = useResetTableRowSelection();
@ -28,6 +30,7 @@ export function TableActionBarButtonDeleteCompanies() {
<EntityTableActionBarButton
label="Delete"
icon={<TbTrash size={16} />}
type="warning"
onClick={handleDeleteClick}
/>
);

View File

@ -28,6 +28,7 @@ import { availableFilters } from './people-filters';
import { TbUser } from 'react-icons/tb';
import { EntityTableActionBar } from '../../components/table/action-bar/EntityTableActionBar';
import { TableActionBarButtonDeletePeople } from './table/TableActionBarButtonDeletePeople';
import { TableActionBarButtonToggleComments } from '../../components/table/action-bar/TableActionBarButtonOpenComments';
const StyledPeopleContainer = styled.div`
display: flex;
@ -93,6 +94,7 @@ function People() {
/>
</StyledPeopleContainer>
<EntityTableActionBar>
<TableActionBarButtonToggleComments />
<TableActionBarButtonDeletePeople />
</EntityTableActionBar>
</>

View File

@ -28,6 +28,7 @@ export function TableActionBarButtonDeletePeople() {
<EntityTableActionBarButton
label="Delete"
icon={<TbTrash size={16} />}
type="warning"
onClick={handleDeleteClick}
/>
);