Refactor UI folder (#2016)
* Added Overview page * Revised Getting Started page * Minor revision * Edited readme, minor modifications to docs * Removed sweep.yaml, .devcontainer, .ergomake * Moved security.md to .github, added contributing.md * changes as per code review * updated contributing.md * fixed broken links & added missing links in doc, improved structure * fixed link in wsl setup * fixed server link, added https cloning in yarn-setup * removed package-lock.json * added doc card, admonitions * removed underline from nav buttons * refactoring modules/ui * refactoring modules/ui * Change folder case * Fix theme location * Fix case 2 * Fix storybook --------- Co-authored-by: Nimra Ahmed <nimra1408@gmail.com> Co-authored-by: Nimra Ahmed <50912134+nimraahmed@users.noreply.github.com>
This commit is contained in:
86
front/src/modules/ui/layout/page/DefaultLayout.tsx
Normal file
86
front/src/modules/ui/layout/page/DefaultLayout.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { AnimatePresence, LayoutGroup } from 'framer-motion';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { AuthModal } from '@/auth/components/Modal';
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { CommandMenu } from '@/command-menu/components/CommandMenu';
|
||||
import { NavbarAnimatedContainer } from '@/ui/navigation/navbar/components/NavbarAnimatedContainer';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
|
||||
import { AppNavbar } from '~/AppNavbar';
|
||||
import { CompaniesMockMode } from '~/pages/companies/CompaniesMockMode';
|
||||
|
||||
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
|
||||
|
||||
const StyledLayout = styled.div`
|
||||
background: ${({ theme }) => theme.background.noisy};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
scrollbar-color: ${({ theme }) => theme.border.color.medium};
|
||||
|
||||
scrollbar-width: 4px;
|
||||
width: 100vw;
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
}
|
||||
`;
|
||||
|
||||
const NAVBAR_WIDTH = '236px';
|
||||
|
||||
const StyledMainContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
width: ${() =>
|
||||
useRecoilValue(isNavbarOpenedState)
|
||||
? `calc(100% - ${NAVBAR_WIDTH})`
|
||||
: '100%'};
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: ${() => (useRecoilValue(isNavbarOpenedState) ? '0' : '100%')};
|
||||
}
|
||||
`;
|
||||
|
||||
type DefaultLayoutProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const DefaultLayout = ({ children }: DefaultLayoutProps) => {
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
|
||||
return (
|
||||
<StyledLayout>
|
||||
<CommandMenu />
|
||||
<NavbarAnimatedContainer>
|
||||
<AppNavbar />
|
||||
</NavbarAnimatedContainer>
|
||||
<StyledMainContainer>
|
||||
{onboardingStatus && onboardingStatus !== OnboardingStatus.Completed ? (
|
||||
<>
|
||||
<CompaniesMockMode />
|
||||
<AnimatePresence mode="wait">
|
||||
<LayoutGroup>
|
||||
<AuthModal>{children}</AuthModal>
|
||||
</LayoutGroup>
|
||||
</AnimatePresence>
|
||||
</>
|
||||
) : (
|
||||
<>{children}</>
|
||||
)}
|
||||
</StyledMainContainer>
|
||||
</StyledLayout>
|
||||
);
|
||||
};
|
||||
18
front/src/modules/ui/layout/page/PageAddButton.tsx
Normal file
18
front/src/modules/ui/layout/page/PageAddButton.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
import { IconButton } from '@/ui/input/button/components/IconButton';
|
||||
|
||||
type PageAddButtonProps = {
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
export const PageAddButton = ({ onClick }: PageAddButtonProps) => (
|
||||
<IconButton
|
||||
Icon={IconPlus}
|
||||
dataTestId="add-button"
|
||||
size="medium"
|
||||
variant="secondary"
|
||||
accent="default"
|
||||
onClick={onClick}
|
||||
ariaLabel="Add"
|
||||
/>
|
||||
);
|
||||
12
front/src/modules/ui/layout/page/PageBody.tsx
Normal file
12
front/src/modules/ui/layout/page/PageBody.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { PAGE_BAR_MIN_HEIGHT } from './PageHeader';
|
||||
import { RightDrawerContainer } from './RightDrawerContainer';
|
||||
|
||||
type PageBodyProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
};
|
||||
|
||||
export const PageBody = ({ children }: PageBodyProps) => (
|
||||
<RightDrawerContainer topMargin={PAGE_BAR_MIN_HEIGHT + 16 + 16}>
|
||||
{children}
|
||||
</RightDrawerContainer>
|
||||
);
|
||||
15
front/src/modules/ui/layout/page/PageContainer.tsx
Normal file
15
front/src/modules/ui/layout/page/PageContainer.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type PageContainerProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const PageContainer = ({ children }: PageContainerProps) => (
|
||||
<StyledContainer>{children}</StyledContainer>
|
||||
);
|
||||
21
front/src/modules/ui/layout/page/PageFavoriteButton.tsx
Normal file
21
front/src/modules/ui/layout/page/PageFavoriteButton.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { IconHeart } from '@/ui/display/icon';
|
||||
import { IconButton } from '@/ui/input/button/components/IconButton';
|
||||
|
||||
type PageFavoriteButtonProps = {
|
||||
isFavorite: boolean;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
export const PageFavoriteButton = ({
|
||||
isFavorite,
|
||||
onClick,
|
||||
}: PageFavoriteButtonProps) => (
|
||||
<IconButton
|
||||
Icon={IconHeart}
|
||||
size="medium"
|
||||
variant="secondary"
|
||||
data-testid="add-button"
|
||||
accent={isFavorite ? 'danger' : 'default'}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
124
front/src/modules/ui/layout/page/PageHeader.tsx
Normal file
124
front/src/modules/ui/layout/page/PageHeader.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
import { ComponentProps, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { IconChevronLeft } from '@/ui/display/icon/index';
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import {
|
||||
IconButton,
|
||||
IconButtonSize,
|
||||
} from '@/ui/input/button/components/IconButton';
|
||||
import NavCollapseButton from '@/ui/navigation/navbar/components/NavCollapseButton';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
|
||||
|
||||
export const PAGE_BAR_MIN_HEIGHT = 40;
|
||||
|
||||
const StyledTopBarContainer = styled.div`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.noisy};
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
justify-content: space-between;
|
||||
min-height: ${PAGE_BAR_MIN_HEIGHT}px;
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
padding-left: 0;
|
||||
padding-right: ${({ theme }) => theme.spacing(3)};
|
||||
z-index: 20;
|
||||
`;
|
||||
|
||||
const StyledLeftContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTitleContainer = styled.div`
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
max-width: 50%;
|
||||
`;
|
||||
|
||||
const StyledTopBarButtonContainer = styled.div`
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledBackIconButton = styled(IconButton)`
|
||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledTopBarIconStyledTitleContainer = styled.div<{
|
||||
hideLeftPadding?: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-left: ${({ theme, hideLeftPadding }) =>
|
||||
hideLeftPadding ? theme.spacing(2) : undefined};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledPageActionContainer = styled.div`
|
||||
display: inline-flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type PageHeaderProps = ComponentProps<'div'> & {
|
||||
title: string;
|
||||
hasBackButton?: boolean;
|
||||
Icon: IconComponent;
|
||||
children?: JSX.Element | JSX.Element[];
|
||||
};
|
||||
|
||||
export const PageHeader = ({
|
||||
title,
|
||||
hasBackButton,
|
||||
Icon,
|
||||
children,
|
||||
}: PageHeaderProps) => {
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = useCallback(() => navigate(-1), [navigate]);
|
||||
|
||||
const isNavbarOpened = useRecoilValue(isNavbarOpenedState);
|
||||
|
||||
const iconSize: IconButtonSize = useIsMobile() ? 'small' : 'medium';
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledTopBarContainer>
|
||||
<StyledLeftContainer>
|
||||
{!isNavbarOpened && (
|
||||
<StyledTopBarButtonContainer>
|
||||
<NavCollapseButton direction="right" />
|
||||
</StyledTopBarButtonContainer>
|
||||
)}
|
||||
{hasBackButton && (
|
||||
<StyledTopBarButtonContainer>
|
||||
<StyledBackIconButton
|
||||
Icon={IconChevronLeft}
|
||||
size={iconSize}
|
||||
onClick={navigateBack}
|
||||
variant="tertiary"
|
||||
/>
|
||||
</StyledTopBarButtonContainer>
|
||||
)}
|
||||
<StyledTopBarIconStyledTitleContainer hideLeftPadding={!hasBackButton}>
|
||||
{Icon && <Icon size={theme.icon.size.md} />}
|
||||
<StyledTitleContainer data-testid="top-bar-title">
|
||||
<OverflowingTextWithTooltip text={title} />
|
||||
</StyledTitleContainer>
|
||||
</StyledTopBarIconStyledTitleContainer>
|
||||
</StyledLeftContainer>
|
||||
<StyledPageActionContainer>{children}</StyledPageActionContainer>
|
||||
</StyledTopBarContainer>
|
||||
);
|
||||
};
|
||||
16
front/src/modules/ui/layout/page/PageHotkeysEffect.tsx
Normal file
16
front/src/modules/ui/layout/page/PageHotkeysEffect.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { TableHotkeyScope } from '@/ui/data/data-table/types/TableHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
|
||||
type PageHotkeysEffectProps = {
|
||||
onAddButtonClick?: () => void;
|
||||
};
|
||||
|
||||
export const PageHotkeysEffect = ({
|
||||
onAddButtonClick,
|
||||
}: PageHotkeysEffectProps) => {
|
||||
useScopedHotkeys('c', () => onAddButtonClick?.(), TableHotkeyScope.Table, [
|
||||
onAddButtonClick,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
17
front/src/modules/ui/layout/page/PagePanel.tsx
Normal file
17
front/src/modules/ui/layout/page/PagePanel.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledPanel = styled.div`
|
||||
background: ${({ theme }) => theme.background.primary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const PagePanel = ({ children }: { children: React.ReactNode }) => (
|
||||
<StyledPanel>{children}</StyledPanel>
|
||||
);
|
||||
46
front/src/modules/ui/layout/page/RightDrawerContainer.tsx
Normal file
46
front/src/modules/ui/layout/page/RightDrawerContainer.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { RightDrawer } from '@/ui/layout/right-drawer/components/RightDrawer';
|
||||
|
||||
import { PagePanel } from './PagePanel';
|
||||
|
||||
type RightDrawerContainerProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
topMargin?: number;
|
||||
};
|
||||
|
||||
const StyledMainContainer = styled.div<{ topMargin: number }>`
|
||||
background: ${({ theme }) => theme.background.noisy};
|
||||
display: flex;
|
||||
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
height: calc(100% - ${(props) => props.topMargin}px);
|
||||
|
||||
padding-bottom: ${({ theme }) => theme.spacing(3)};
|
||||
padding-right: ${({ theme }) => theme.spacing(3)};
|
||||
width: calc(100% - ${({ theme }) => theme.spacing(3)});
|
||||
`;
|
||||
|
||||
type LeftContainerProps = {
|
||||
isRightDrawerOpen?: boolean;
|
||||
};
|
||||
|
||||
const StyledLeftContainer = styled.div<LeftContainerProps>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const RightDrawerContainer = ({
|
||||
children,
|
||||
topMargin,
|
||||
}: RightDrawerContainerProps) => (
|
||||
<StyledMainContainer topMargin={topMargin ?? 0}>
|
||||
<StyledLeftContainer>
|
||||
<PagePanel>{children}</PagePanel>
|
||||
</StyledLeftContainer>
|
||||
<RightDrawer />
|
||||
</StyledMainContainer>
|
||||
);
|
||||
44
front/src/modules/ui/layout/page/ShowPageContainer.tsx
Normal file
44
front/src/modules/ui/layout/page/ShowPageContainer.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { ReactElement } from 'react';
|
||||
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`
|
||||
display: flex;
|
||||
|
||||
gap: ${({ theme }) => (useIsMobile() ? theme.spacing(3) : '0')};
|
||||
height: ${() => (useIsMobile() ? '100%' : 'auto')};
|
||||
overflow-x: ${() => (useIsMobile() ? 'hidden' : 'auto')};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledInnerContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: ${() => (useIsMobile() ? 'column' : 'row')};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledScrollWrapper = styled(ScrollWrapper)`
|
||||
background-color: ${({ theme }) => theme.background.secondary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
`;
|
||||
|
||||
export type ShowPageContainerProps = {
|
||||
children: ReactElement[];
|
||||
};
|
||||
|
||||
export const ShowPageContainer = ({ children }: ShowPageContainerProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
return isMobile ? (
|
||||
<StyledOuterContainer>
|
||||
<StyledScrollWrapper>
|
||||
<StyledInnerContainer>{children}</StyledInnerContainer>
|
||||
</StyledScrollWrapper>
|
||||
</StyledOuterContainer>
|
||||
) : (
|
||||
<StyledOuterContainer>
|
||||
<StyledInnerContainer>{children}</StyledInnerContainer>
|
||||
</StyledOuterContainer>
|
||||
);
|
||||
};
|
||||
36
front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx
Normal file
36
front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { JSX } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
import { PageHeader } from './PageHeader';
|
||||
import { RightDrawerContainer } from './RightDrawerContainer';
|
||||
|
||||
type SubMenuTopBarContainerProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
title: string;
|
||||
Icon: IconComponent;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div<{ isMobile: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: ${({ theme, isMobile }) => (!isMobile ? theme.spacing(4) : 0)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const SubMenuTopBarContainer = ({
|
||||
children,
|
||||
title,
|
||||
Icon,
|
||||
}: SubMenuTopBarContainerProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<StyledContainer isMobile={isMobile}>
|
||||
{isMobile && <PageHeader title={title} Icon={Icon} />}
|
||||
<RightDrawerContainer topMargin={16}>{children}</RightDrawerContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user