From cb259d5cf10423933a0f67dc33f04975b2a00adc Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Tue, 30 May 2023 21:03:50 +0200 Subject: [PATCH] Feat/add right drawer (#159) * Added right drawer component and logic * Refactored layout to accept right drawer --- front/src/App.tsx | 4 +- .../comments/RightDrawerComments.tsx | 9 +++ .../table/action-bar/EntityTableActionBar.tsx | 1 - .../action-bar/EntityTableActionBarButton.tsx | 19 +++++-- .../TableActionBarButtonOpenComments.tsx | 19 +++++++ front/src/layout/AppLayout.tsx | 10 ++-- front/src/layout/Panel.tsx | 14 +++++ front/src/layout/containers/MainContainer.tsx | 12 ++++ .../layout/containers/WithTopBarContainer.tsx | 55 +++++++++++-------- front/src/layout/navbar/Navbar.tsx | 8 +-- front/src/layout/right-drawer/RightDrawer.tsx | 30 ++++++++++ .../layout/right-drawer/RightDrawerRouter.tsx | 14 +++++ .../layout/right-drawer/RightDrawerTopBar.tsx | 34 ++++++++++++ .../RightDrawerTopBarCloseButton.tsx | 37 +++++++++++++ front/src/layout/styles/themes.ts | 8 +++ front/src/layout/top-bar/TopBar.tsx | 1 - .../right-drawer/hooks/useOpenRightDrawer.ts | 14 +++++ .../states/isRightDrawerOpenState.ts | 6 ++ .../states/rightDrawerPageState.ts | 7 +++ .../right-drawer/types/RightDrawerPage.ts | 1 + front/src/pages/companies/Companies.tsx | 2 + .../TableActionBarButtonDeleteCompanies.tsx | 3 + front/src/pages/people/People.tsx | 2 + .../TableActionBarButtonDeletePeople.tsx | 1 + 24 files changed, 272 insertions(+), 39 deletions(-) create mode 100644 front/src/components/comments/RightDrawerComments.tsx create mode 100644 front/src/components/table/action-bar/TableActionBarButtonOpenComments.tsx create mode 100644 front/src/layout/Panel.tsx create mode 100644 front/src/layout/containers/MainContainer.tsx create mode 100644 front/src/layout/right-drawer/RightDrawer.tsx create mode 100644 front/src/layout/right-drawer/RightDrawerRouter.tsx create mode 100644 front/src/layout/right-drawer/RightDrawerTopBar.tsx create mode 100644 front/src/layout/right-drawer/RightDrawerTopBarCloseButton.tsx create mode 100644 front/src/modules/ui/layout/right-drawer/hooks/useOpenRightDrawer.ts create mode 100644 front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts create mode 100644 front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts create mode 100644 front/src/modules/ui/layout/right-drawer/types/RightDrawerPage.ts diff --git a/front/src/App.tsx b/front/src/App.tsx index 378bd06e3..8cb19a087 100644 --- a/front/src/App.tsx +++ b/front/src/App.tsx @@ -24,7 +24,7 @@ function App() { }, [data]); return ( -
+ <> { @@ -65,7 +65,7 @@ function App() { } -
+ ); } diff --git a/front/src/components/comments/RightDrawerComments.tsx b/front/src/components/comments/RightDrawerComments.tsx new file mode 100644 index 000000000..13372bc79 --- /dev/null +++ b/front/src/components/comments/RightDrawerComments.tsx @@ -0,0 +1,9 @@ +import { RightDrawerTopBar } from '../../layout/right-drawer/RightDrawerTopBar'; + +export function RightDrawerComments() { + return ( + <> + + + ); +} diff --git a/front/src/components/table/action-bar/EntityTableActionBar.tsx b/front/src/components/table/action-bar/EntityTableActionBar.tsx index 70b322bdf..b9747af8a 100644 --- a/front/src/components/table/action-bar/EntityTableActionBar.tsx +++ b/front/src/components/table/action-bar/EntityTableActionBar.tsx @@ -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%); diff --git a/front/src/components/table/action-bar/EntityTableActionBarButton.tsx b/front/src/components/table/action-bar/EntityTableActionBarButton.tsx index dbd1386af..d7923b1c0 100644 --- a/front/src/components/table/action-bar/EntityTableActionBarButton.tsx +++ b/front/src/components/table/action-bar/EntityTableActionBarButton.tsx @@ -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` 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 ( - + {icon} {label} diff --git a/front/src/components/table/action-bar/TableActionBarButtonOpenComments.tsx b/front/src/components/table/action-bar/TableActionBarButtonOpenComments.tsx new file mode 100644 index 000000000..50d100775 --- /dev/null +++ b/front/src/components/table/action-bar/TableActionBarButtonOpenComments.tsx @@ -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 ( + } + onClick={handleButtonClick} + /> + ); +} diff --git a/front/src/layout/AppLayout.tsx b/front/src/layout/AppLayout.tsx index 2bf7c7b39..29a507c7b 100644 --- a/front/src/layout/AppLayout.tsx +++ b/front/src/layout/AppLayout.tsx @@ -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) { - {children} + {children} ); diff --git a/front/src/layout/Panel.tsx b/front/src/layout/Panel.tsx new file mode 100644 index 000000000..c0deaa998 --- /dev/null +++ b/front/src/layout/Panel.tsx @@ -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 {children}; +} diff --git a/front/src/layout/containers/MainContainer.tsx b/front/src/layout/containers/MainContainer.tsx new file mode 100644 index 000000000..30ca40390 --- /dev/null +++ b/front/src/layout/containers/MainContainer.tsx @@ -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 {children}; +} diff --git a/front/src/layout/containers/WithTopBarContainer.tsx b/front/src/layout/containers/WithTopBarContainer.tsx index 6dcf001b9..f3696a701 100644 --- a/front/src/layout/containers/WithTopBarContainer.tsx +++ b/front/src/layout/containers/WithTopBarContainer.tsx @@ -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` + 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 ( - - {children} - + + + {children} + + + ); } diff --git a/front/src/layout/navbar/Navbar.tsx b/front/src/layout/navbar/Navbar.tsx index 1173700fc..2223153b7 100644 --- a/front/src/layout/navbar/Navbar.tsx +++ b/front/src/layout/navbar/Navbar.tsx @@ -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 ( <> @@ -60,5 +60,3 @@ function Navbar({ workspace }: OwnProps) { ); } - -export default Navbar; diff --git a/front/src/layout/right-drawer/RightDrawer.tsx b/front/src/layout/right-drawer/RightDrawer.tsx new file mode 100644 index 000000000..7a97bc5f1 --- /dev/null +++ b/front/src/layout/right-drawer/RightDrawer.tsx @@ -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 ( + + + + + + ); +} diff --git a/front/src/layout/right-drawer/RightDrawerRouter.tsx b/front/src/layout/right-drawer/RightDrawerRouter.tsx new file mode 100644 index 000000000..843dbe470 --- /dev/null +++ b/front/src/layout/right-drawer/RightDrawerRouter.tsx @@ -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' ? : <>; +} diff --git a/front/src/layout/right-drawer/RightDrawerTopBar.tsx b/front/src/layout/right-drawer/RightDrawerTopBar.tsx new file mode 100644 index 000000000..fcb0ba7ff --- /dev/null +++ b/front/src/layout/right-drawer/RightDrawerTopBar.tsx @@ -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 ( + + {title} + + + ); +} diff --git a/front/src/layout/right-drawer/RightDrawerTopBarCloseButton.tsx b/front/src/layout/right-drawer/RightDrawerTopBarCloseButton.tsx new file mode 100644 index 000000000..4f507dec7 --- /dev/null +++ b/front/src/layout/right-drawer/RightDrawerTopBarCloseButton.tsx @@ -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 ( + + + + ); +} diff --git a/front/src/layout/styles/themes.ts b/front/src/layout/styles/themes.ts index bbe14b601..7e3486f12 100644 --- a/front/src/layout/styles/themes.ts +++ b/front/src/layout/styles/themes.ts @@ -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', diff --git a/front/src/layout/top-bar/TopBar.tsx b/front/src/layout/top-bar/TopBar.tsx index eb790c21d..9d3eda056 100644 --- a/front/src/layout/top-bar/TopBar.tsx +++ b/front/src/layout/top-bar/TopBar.tsx @@ -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` diff --git a/front/src/modules/ui/layout/right-drawer/hooks/useOpenRightDrawer.ts b/front/src/modules/ui/layout/right-drawer/hooks/useOpenRightDrawer.ts new file mode 100644 index 000000000..dbce2d034 --- /dev/null +++ b/front/src/modules/ui/layout/right-drawer/hooks/useOpenRightDrawer.ts @@ -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); + }; +} diff --git a/front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts b/front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts new file mode 100644 index 000000000..fcfd62975 --- /dev/null +++ b/front/src/modules/ui/layout/right-drawer/states/isRightDrawerOpenState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const isRightDrawerOpenState = atom({ + key: 'ui/layout/is-right-drawer-open', + default: false, +}); diff --git a/front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts b/front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts new file mode 100644 index 000000000..12641d91a --- /dev/null +++ b/front/src/modules/ui/layout/right-drawer/states/rightDrawerPageState.ts @@ -0,0 +1,7 @@ +import { atom } from 'recoil'; +import { RightDrawerPage } from '../types/RightDrawerPage'; + +export const rightDrawerPageState = atom({ + key: 'ui/layout/right-drawer-page', + default: 'comments', +}); diff --git a/front/src/modules/ui/layout/right-drawer/types/RightDrawerPage.ts b/front/src/modules/ui/layout/right-drawer/types/RightDrawerPage.ts new file mode 100644 index 000000000..8fc92dbce --- /dev/null +++ b/front/src/modules/ui/layout/right-drawer/types/RightDrawerPage.ts @@ -0,0 +1 @@ +export type RightDrawerPage = 'comments'; diff --git a/front/src/pages/companies/Companies.tsx b/front/src/pages/companies/Companies.tsx index a27c9925e..306697e90 100644 --- a/front/src/pages/companies/Companies.tsx +++ b/front/src/pages/companies/Companies.tsx @@ -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() { /> + diff --git a/front/src/pages/companies/table/TableActionBarButtonDeleteCompanies.tsx b/front/src/pages/companies/table/TableActionBarButtonDeleteCompanies.tsx index fb8fa0f3a..4b4375bd4 100644 --- a/front/src/pages/companies/table/TableActionBarButtonDeleteCompanies.tsx +++ b/front/src/pages/companies/table/TableActionBarButtonDeleteCompanies.tsx @@ -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() { } + type="warning" onClick={handleDeleteClick} /> ); diff --git a/front/src/pages/people/People.tsx b/front/src/pages/people/People.tsx index 8610ec0ee..eb49371d7 100644 --- a/front/src/pages/people/People.tsx +++ b/front/src/pages/people/People.tsx @@ -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() { /> + diff --git a/front/src/pages/people/table/TableActionBarButtonDeletePeople.tsx b/front/src/pages/people/table/TableActionBarButtonDeletePeople.tsx index 0d5c73ca0..8d0740469 100644 --- a/front/src/pages/people/table/TableActionBarButtonDeletePeople.tsx +++ b/front/src/pages/people/table/TableActionBarButtonDeletePeople.tsx @@ -28,6 +28,7 @@ export function TableActionBarButtonDeletePeople() { } + type="warning" onClick={handleDeleteClick} /> );