Uniformize folder structure (#693)
* Uniformize folder structure * Fix icons * Fix icons * Fix tests * Fix tests
This commit is contained in:
@ -3,12 +3,11 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { CommandMenu } from '@/command-menu/components/CommandMenu';
|
||||
import { NavbarAnimatedContainer } from '@/ui/navbar/components/NavbarAnimatedContainer';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
|
||||
import { AppNavbar } from '~/AppNavbar';
|
||||
|
||||
import { MOBILE_VIEWPORT } from '../themes/themes';
|
||||
|
||||
import { NavbarAnimatedContainer } from './navbar/NavbarAnimatedContainer';
|
||||
import { isNavbarOpenedState } from './states/isNavbarOpenedState';
|
||||
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
|
||||
|
||||
const StyledLayout = styled.div`
|
||||
background: ${({ theme }) => theme.background.noisy};
|
||||
@ -1,7 +1,8 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { Panel } from '../Panel';
|
||||
import { RightDrawer } from '../right-drawer/components/RightDrawer';
|
||||
import { RightDrawer } from '@/ui/right-drawer/components/RightDrawer';
|
||||
|
||||
import { Panel } from './Panel';
|
||||
|
||||
type OwnProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
@ -1,8 +1,8 @@
|
||||
import { ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { TopBarHotkeys } from '../top-bar/TableTopBarHotkeys';
|
||||
import { TOP_BAR_MIN_HEIGHT, TopBar } from '../top-bar/TopBar';
|
||||
import { TopBarHotkeys } from '../top-bar/components/TableTopBarHotkeys';
|
||||
import { TOP_BAR_MIN_HEIGHT, TopBar } from '../top-bar/components/TopBar';
|
||||
|
||||
import { RightDrawerContainer } from './RightDrawerContainer';
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { Panel } from '../Panel';
|
||||
import { RightDrawer } from '../right-drawer/components/RightDrawer';
|
||||
import { isRightDrawerOpenState } from '../right-drawer/states/isRightDrawerOpenState';
|
||||
|
||||
type OwnProps = {
|
||||
children: 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;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function ContentContainer({ children, topMargin }: OwnProps) {
|
||||
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
||||
|
||||
return (
|
||||
<StyledMainContainer topMargin={topMargin ?? 0}>
|
||||
<StyledLeftContainer isRightDrawerOpen={isRightDrawerOpen}>
|
||||
<Panel>{children}</Panel>
|
||||
</StyledLeftContainer>
|
||||
<RightDrawer />
|
||||
</StyledMainContainer>
|
||||
);
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const FlexExpandingContainer = styled.div`
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
@ -1,15 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function VerticalFullWidthContainer({
|
||||
children,
|
||||
}: {
|
||||
children: JSX.Element[];
|
||||
}) {
|
||||
return <StyledContainer>{children}</StyledContainer>;
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import NavItemsContainer from './NavItemsContainer';
|
||||
import NavWorkspaceButton from './NavWorkspaceButton';
|
||||
|
||||
type OwnProps = {
|
||||
children: JSX.Element;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 220px;
|
||||
`;
|
||||
|
||||
export default function MainNavbar({ children }: OwnProps) {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<NavWorkspaceButton />
|
||||
<NavItemsContainer>{children}</NavItemsContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
IconLayoutSidebarLeftCollapse,
|
||||
IconLayoutSidebarRightCollapse,
|
||||
} from '@/ui/icons';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
|
||||
|
||||
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
|
||||
|
||||
const CollapseButton = styled.button<{ hideOnDesktop: boolean | undefined }>`
|
||||
align-items: center;
|
||||
background: inherit;
|
||||
border: 0;
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.background.quaternary};
|
||||
}
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
height: 32px;
|
||||
justify-content: center;
|
||||
|
||||
padding: 0;
|
||||
user-select: none;
|
||||
|
||||
width: 32px;
|
||||
|
||||
${(props) =>
|
||||
props.hideOnDesktop &&
|
||||
`@media (min-width: ${MOBILE_VIEWPORT}px) {
|
||||
display:none;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
type CollapseButtonProps = {
|
||||
hideIfOpen?: boolean;
|
||||
hideIfClosed?: boolean;
|
||||
hideOnDesktop?: boolean;
|
||||
};
|
||||
|
||||
export default function NavCollapseButton({
|
||||
hideIfOpen,
|
||||
hideOnDesktop,
|
||||
}: CollapseButtonProps) {
|
||||
const [isNavOpen, setIsNavOpen] = useRecoilState(isNavbarOpenedState);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isNavOpen && !hideIfOpen && (
|
||||
<CollapseButton
|
||||
onClick={() => setIsNavOpen(!isNavOpen)}
|
||||
hideOnDesktop={hideOnDesktop}
|
||||
>
|
||||
<IconLayoutSidebarLeftCollapse size={16} />
|
||||
</CollapseButton>
|
||||
)}
|
||||
{!isNavOpen && (
|
||||
<CollapseButton
|
||||
onClick={() => setIsNavOpen(!isNavOpen)}
|
||||
hideOnDesktop={hideOnDesktop}
|
||||
>
|
||||
<IconLayoutSidebarRightCollapse size={16} />
|
||||
</CollapseButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
|
||||
|
||||
type OwnProps = {
|
||||
label: string;
|
||||
to?: string;
|
||||
onClick?: () => void;
|
||||
active?: boolean;
|
||||
icon: ReactNode;
|
||||
danger?: boolean;
|
||||
soon?: boolean;
|
||||
};
|
||||
|
||||
type StyledItemProps = {
|
||||
active?: boolean;
|
||||
danger?: boolean;
|
||||
soon?: boolean;
|
||||
};
|
||||
|
||||
const StyledItem = styled.button<StyledItemProps>`
|
||||
align-items: center;
|
||||
background: ${(props) =>
|
||||
props.active ? props.theme.background.transparent.light : 'inherit'};
|
||||
border: none;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${(props) => {
|
||||
if (props.active) {
|
||||
return props.theme.font.color.primary;
|
||||
}
|
||||
if (props.danger) {
|
||||
return props.theme.color.red;
|
||||
}
|
||||
if (props.soon) {
|
||||
return props.theme.font.color.light;
|
||||
}
|
||||
return props.theme.font.color.secondary;
|
||||
}};
|
||||
cursor: ${(props) => (props.soon ? 'default' : 'pointer')};
|
||||
display: flex;
|
||||
font-family: 'Inter';
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
margin-bottom: calc(${({ theme }) => theme.spacing(1)} / 2);
|
||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||
pointer-events: ${(props) => (props.soon ? 'none' : 'auto')};
|
||||
:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
color: ${(props) =>
|
||||
props.danger ? props.theme.color.red : props.theme.font.color.primary};
|
||||
}
|
||||
user-select: none;
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledItemLabel = styled.div`
|
||||
display: flex;
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledSoonPill = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: 50px;
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
height: 16px;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
function NavItem({ label, icon, to, onClick, active, danger, soon }: OwnProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onItemClick = () => {
|
||||
if (onClick) {
|
||||
onClick();
|
||||
return;
|
||||
}
|
||||
if (to) {
|
||||
navigate(to);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledItem
|
||||
onClick={onItemClick}
|
||||
active={active}
|
||||
aria-selected={active}
|
||||
danger={danger}
|
||||
soon={soon}
|
||||
>
|
||||
{icon}
|
||||
<StyledItemLabel>{label}</StyledItemLabel>
|
||||
{soon && <StyledSoonPill>Soon</StyledSoonPill>}
|
||||
</StyledItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavItem;
|
||||
@ -1,17 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type OwnProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const StyledNavItemsContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 40px;
|
||||
`;
|
||||
|
||||
function NavItemsContainer({ children }: OwnProps) {
|
||||
return <StyledNavItemsContainer>{children}</StyledNavItemsContainer>;
|
||||
}
|
||||
|
||||
export default NavItemsContainer;
|
||||
@ -1,22 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type OwnProps = {
|
||||
label: string;
|
||||
};
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
padding-top: ${({ theme }) => theme.spacing(8)};
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
function NavTitle({ label }: OwnProps) {
|
||||
return <StyledTitle>{label}</StyledTitle>;
|
||||
}
|
||||
|
||||
export default NavTitle;
|
||||
@ -1,73 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
|
||||
|
||||
import NavCollapseButton from './NavCollapseButton';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
align-self: flex-start;
|
||||
background: inherit;
|
||||
border: 0;
|
||||
display: flex;
|
||||
height: 34px;
|
||||
justify-content: space-between;
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const LogoAndNameContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
type StyledLogoProps = {
|
||||
logo?: string | null;
|
||||
};
|
||||
|
||||
const StyledLogo = styled.div<StyledLogoProps>`
|
||||
background: url(${(props) => props.logo});
|
||||
background-size: cover;
|
||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
`;
|
||||
|
||||
const StyledName = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-family: 'Inter';
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
function NavWorkspaceButton() {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
|
||||
const currentWorkspace = currentUser?.workspaceMember?.workspace;
|
||||
const DEFAULT_LOGO =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<LogoAndNameContainer>
|
||||
<StyledLogo
|
||||
logo={
|
||||
currentWorkspace?.logo
|
||||
? getImageAbsoluteURIOrBase64(currentWorkspace.logo)
|
||||
: DEFAULT_LOGO
|
||||
}
|
||||
></StyledLogo>
|
||||
<StyledName>{currentWorkspace?.displayName ?? 'Twenty'}</StyledName>
|
||||
</LogoAndNameContainer>
|
||||
<NavCollapseButton />
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavWorkspaceButton;
|
||||
@ -1,57 +0,0 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
|
||||
|
||||
import { useIsSubNavbarDisplayed } from '../hooks/useIsSubNavbarDisplayed';
|
||||
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
|
||||
import { isNavbarSwitchingSizeState } from '../states/isNavbarSwitchingSizeState';
|
||||
|
||||
const StyledNavbarContainer = styled(motion.div)`
|
||||
align-items: end;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: ${(props) =>
|
||||
useRecoilValue(isNavbarOpenedState)
|
||||
? `calc(100% - ` + props.theme.spacing(4) + `)`
|
||||
: '0'};
|
||||
}
|
||||
`;
|
||||
|
||||
type NavbarProps = {
|
||||
children: React.ReactNode;
|
||||
layout?: string;
|
||||
};
|
||||
|
||||
export function NavbarAnimatedContainer({ children, layout }: NavbarProps) {
|
||||
const isMenuOpened = useRecoilValue(isNavbarOpenedState);
|
||||
const [, setIsNavbarSwitchingSize] = useRecoilState(
|
||||
isNavbarSwitchingSizeState,
|
||||
);
|
||||
const isSubNavbarDisplayed = useIsSubNavbarDisplayed();
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledNavbarContainer
|
||||
onAnimationComplete={() => {
|
||||
setIsNavbarSwitchingSize(false);
|
||||
}}
|
||||
animate={{
|
||||
width: isMenuOpened ? (isSubNavbarDisplayed ? '520px' : '220px') : '0',
|
||||
opacity: isMenuOpened ? 1 : 0,
|
||||
}}
|
||||
transition={{
|
||||
duration: theme.animation.duration.visible,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</StyledNavbarContainer>
|
||||
);
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { IconChevronLeft } from '@/ui/icons/index';
|
||||
|
||||
import { isNavbarSwitchingSizeState } from '../../states/isNavbarSwitchingSizeState';
|
||||
import NavCollapseButton from '../NavCollapseButton';
|
||||
|
||||
type OwnProps = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
const IconAndButtonContainer = styled.button`
|
||||
align-items: center;
|
||||
background: inherit;
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
export default function NavBackButton({ title }: OwnProps) {
|
||||
const navigate = useNavigate();
|
||||
const [, setIsNavbarSwitchingSize] = useRecoilState(
|
||||
isNavbarSwitchingSizeState,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledContainer>
|
||||
<IconAndButtonContainer
|
||||
onClick={() => {
|
||||
setIsNavbarSwitchingSize(true);
|
||||
navigate('/', { replace: true });
|
||||
}}
|
||||
>
|
||||
<IconChevronLeft />
|
||||
<span>{title}</span>
|
||||
</IconAndButtonContainer>
|
||||
<NavCollapseButton hideOnDesktop={true} />
|
||||
</StyledContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import NavItemsContainer from '../NavItemsContainer';
|
||||
|
||||
import NavBackButton from './NavBackButton';
|
||||
|
||||
type OwnProps = {
|
||||
children: JSX.Element;
|
||||
backButtonTitle: string;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: ${({ theme }) => theme.spacing(6)};
|
||||
width: 220px;
|
||||
`;
|
||||
|
||||
export default function SubNavbar({ children, backButtonTitle }: OwnProps) {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<NavBackButton title={backButtonTitle} />
|
||||
<NavItemsContainer>{children}</NavItemsContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
import { useRef } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
OutsideClickAlerterMode,
|
||||
useOutsideAlerter,
|
||||
} from '@/ui/hooks/useOutsideAlerter';
|
||||
import { isDefined } from '@/utils/type-guards/isDefined';
|
||||
|
||||
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
|
||||
import { rightDrawerPageState } from '../states/rightDrawerPageState';
|
||||
|
||||
import { RightDrawerRouter } from './RightDrawerRouter';
|
||||
|
||||
const StyledContainer = styled(motion.div)`
|
||||
background: ${({ theme }) => theme.background.primary};
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
position: fixed;
|
||||
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
`;
|
||||
|
||||
const StyledRightDrawer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function RightDrawer() {
|
||||
const [isRightDrawerOpen, setIsRightDrawerOpen] = useRecoilState(
|
||||
isRightDrawerOpenState,
|
||||
);
|
||||
|
||||
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
|
||||
|
||||
const rightDrawerRef = useRef(null);
|
||||
useOutsideAlerter({
|
||||
ref: rightDrawerRef,
|
||||
callback: () => setIsRightDrawerOpen(false),
|
||||
mode: OutsideClickAlerterMode.absolute,
|
||||
});
|
||||
const theme = useTheme();
|
||||
if (!isDefined(rightDrawerPage)) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledContainer
|
||||
animate={{
|
||||
width: isRightDrawerOpen ? theme.rightDrawerWidth : '0',
|
||||
}}
|
||||
transition={{
|
||||
duration: theme.animation.duration.visible,
|
||||
}}
|
||||
>
|
||||
<StyledRightDrawer ref={rightDrawerRef}>
|
||||
<RightDrawerRouter />
|
||||
</StyledRightDrawer>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const RightDrawerBody = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(
|
||||
100vh - ${({ theme }) => theme.spacing(14)} - 1px
|
||||
); // (-1 for border)
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
`;
|
||||
@ -1,8 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const RightDrawerPage = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
@ -1,28 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { RightDrawerCreateCommentThread } from '@/comments/components/right-drawer/create/RightDrawerCreateCommentThread';
|
||||
import { RightDrawerEditCommentThread } from '@/comments/components/right-drawer/edit/RightDrawerEditCommentThread';
|
||||
import { RightDrawerTimeline } from '@/comments/components/right-drawer/RightDrawerTimeline';
|
||||
import { isDefined } from '@/utils/type-guards/isDefined';
|
||||
|
||||
import { rightDrawerPageState } from '../states/rightDrawerPageState';
|
||||
import { RightDrawerPages } from '../types/RightDrawerPages';
|
||||
|
||||
export function RightDrawerRouter() {
|
||||
const [rightDrawerPage] = useRecoilState(rightDrawerPageState);
|
||||
|
||||
if (!isDefined(rightDrawerPage)) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
switch (rightDrawerPage) {
|
||||
case RightDrawerPages.Timeline:
|
||||
return <RightDrawerTimeline />;
|
||||
case RightDrawerPages.CreateCommentThread:
|
||||
return <RightDrawerCreateCommentThread />;
|
||||
case RightDrawerPages.EditCommentThread:
|
||||
return <RightDrawerEditCommentThread />;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { RightDrawerTopBarCloseButton } from './RightDrawerTopBarCloseButton';
|
||||
|
||||
const StyledRightDrawerTopBar = styled.div`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
height: 56px;
|
||||
justify-content: space-between;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
title?: string | null | undefined;
|
||||
};
|
||||
|
||||
export function RightDrawerTopBar({ title }: OwnProps) {
|
||||
return (
|
||||
<StyledRightDrawerTopBar>
|
||||
<RightDrawerTopBarCloseButton />
|
||||
</StyledRightDrawerTopBar>
|
||||
);
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { IconChevronsRight } from '@/ui/icons/index';
|
||||
|
||||
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
|
||||
|
||||
const StyledButton = styled.button`
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 24px;
|
||||
padding: 3px;
|
||||
|
||||
transition: ${({ theme }) => theme.clickableElementBackgroundTransition};
|
||||
|
||||
width: 24px;
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
}
|
||||
svg {
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
export function RightDrawerTopBarCloseButton() {
|
||||
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
||||
|
||||
function handleButtonClick() {
|
||||
setIsRightDrawerOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledButton onClick={handleButtonClick}>
|
||||
<IconChevronsRight size={16} />
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
|
||||
import { RightDrawerTopBar } from '../RightDrawerTopBar';
|
||||
|
||||
const meta: Meta<typeof RightDrawerTopBar> = {
|
||||
title: 'UI/RightDrawer/RightDrawerTopBar',
|
||||
component: RightDrawerTopBar,
|
||||
argTypes: {
|
||||
title: {
|
||||
control: { type: 'text' },
|
||||
defaultValue: 'My Title',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RightDrawerTopBar>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<div style={{ width: '500px' }}>
|
||||
<RightDrawerTopBar title={'Title'} />
|
||||
</div>,
|
||||
),
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
actions: { argTypesRegex: '^on.*' },
|
||||
},
|
||||
args: {},
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
|
||||
import { rightDrawerPageState } from '../states/rightDrawerPageState';
|
||||
import { RightDrawerPages } from '../types/RightDrawerPages';
|
||||
|
||||
export function useOpenRightDrawer() {
|
||||
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
||||
const [, setRightDrawerPage] = useRecoilState(rightDrawerPageState);
|
||||
|
||||
return function openRightDrawer(rightDrawerPage: RightDrawerPages) {
|
||||
setRightDrawerPage(rightDrawerPage);
|
||||
setIsRightDrawerOpen(true);
|
||||
};
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const isRightDrawerOpenState = atom<boolean>({
|
||||
key: 'ui/layout/is-right-drawer-open',
|
||||
default: false,
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { RightDrawerPages } from '../types/RightDrawerPages';
|
||||
|
||||
export const rightDrawerPageState = atom<RightDrawerPages | null>({
|
||||
key: 'ui/layout/right-drawer-page',
|
||||
default: null,
|
||||
});
|
||||
@ -1,3 +0,0 @@
|
||||
export enum RightDrawerHotkeyScope {
|
||||
RightDrawer = 'right-drawer',
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
export enum RightDrawerPages {
|
||||
Timeline = 'timeline',
|
||||
CreateCommentThread = 'create-comment-thread',
|
||||
EditCommentThread = 'edit-comment-thread',
|
||||
}
|
||||
@ -7,7 +7,7 @@ import { Avatar } from '@/users/components/Avatar';
|
||||
import {
|
||||
beautifyExactDate,
|
||||
beautifyPastDateRelativeToNow,
|
||||
} from '@/utils/datetime/date-utils';
|
||||
} from '~/utils/date-utils';
|
||||
|
||||
type OwnProps = {
|
||||
id?: string;
|
||||
@ -1,5 +1,5 @@
|
||||
import { useScopedHotkeys } from '@/lib/hotkeys/hooks/useScopedHotkeys';
|
||||
import { TableHotkeyScope } from '@/ui/tables/types/TableHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
type OwnProps = {
|
||||
onAddButtonClick?: () => void;
|
||||
@ -1,10 +1,9 @@
|
||||
import { ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconButton } from '@/ui/components/buttons/IconButton';
|
||||
import { IconPlus } from '@/ui/icons/index';
|
||||
|
||||
import NavCollapseButton from '../navbar/NavCollapseButton';
|
||||
import { IconButton } from '@/ui/button/components/IconButton';
|
||||
import { IconPlus } from '@/ui/icon/index';
|
||||
import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton';
|
||||
|
||||
export const TOP_BAR_MIN_HEIGHT = 40;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import NavCollapseButton from '../navbar/NavCollapseButton';
|
||||
import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton';
|
||||
|
||||
const TitleAndCollapseContainer = styled.div`
|
||||
align-items: center;
|
||||
Reference in New Issue
Block a user